package org.jsola.hr.service.impl;

import com.alibaba.excel.EasyExcel;
import org.jsola.common.BeanKit;
import org.jsola.core.Page;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.ExampleKit;
import org.jsola.hr.common.ExcelKit;
import org.jsola.hr.constant.BalanceTypeEnum;
import org.jsola.hr.constant.EmpSocBackStatusConstant;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.dao.IEmpSocBackRecordRecordDAO;
import org.jsola.hr.dto.*;
import org.jsola.hr.entity.CompanyBalanceRecordDO;
import org.jsola.hr.entity.EmpSocBackRecordAccountDO;
import org.jsola.hr.entity.EmpSocBackRecordInfoDO;
import org.jsola.hr.entity.EmpSocBackRecordRecordDO;
import org.jsola.hr.provider.IUserProviderService;
import org.jsola.hr.query.EmpSocBackRecordInfoQuery;
import org.jsola.hr.query.EmpSocBackRecordRecordQuery;
import org.jsola.hr.service.*;
import org.jsola.hr.service.excel.SocBackRecordImportListener;
import org.jsola.hr.vo.EmpSocBackRecordInfoVO;
import org.jsola.hr.vo.EmpSocBackRecordRecordListVO;
import org.jsola.hr.vo.EmpSocBackRecordRecordVO;
import org.jsola.hr.vo.ImportExcelResultListVO;
import org.jsola.user.core.TokenUser;
import org.jsola.user.entity.UserDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.io.File;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 社保回款记录
 *
 * @author zhr
 */
@Service("hrEmpSocBackRecordRecordServiceImpl")
public class EmpSocBackRecordRecordServiceImpl implements IEmpSocBackRecordRecordService {

    @Autowired
    private IEmpSocBackRecordRecordDAO empSocBackRecordRecordDAO;
    @Autowired
    private IEmpSocBackRecordAccountService empSocBackRecordAccountService;
    @Autowired
    private IEmpSocBackRecordInfoService empSocBackRecordInfoService;
    @Autowired
    private ICompanyBalanceRecordService companyBalanceRecordService;
    @Autowired
    private IUserProviderService userProviderService;
    @Autowired
    private IEmpInfoService empInfoService;


    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public EmpSocBackRecordRecordVO save(EmpSocBackRecordRecordAddDTO empSocBackRecordRecordAddDTO, TokenUser tokenUser) {
        String backName = empSocBackRecordRecordAddDTO.getName();
        // 应收
        BigDecimal totalAmount = empSocBackRecordRecordAddDTO.getTotalAmount();
        // 实收
        BigDecimal realityAmount = empSocBackRecordRecordAddDTO.getRealityAmount();
        if (realityAmount.compareTo(new BigDecimal("0")) < 0) {
            throw new ParamException(backName + " 实还不能小于0!");
        }
        // 转DO
        EmpSocBackRecordRecordDO empSocBackRecordRecordDO = empSocBackRecordRecordAddDTO.to(EmpSocBackRecordRecordDO.class);
        // 修改回款状态 及 实收金额
        EmpSocBackRecordInfoDO empSocBackRecordInfoDO = empSocBackRecordInfoService.selectByEmpIdAndYearAndMonth(
                empSocBackRecordRecordAddDTO.getEmpId(), empSocBackRecordRecordAddDTO.getYear(),
                empSocBackRecordRecordAddDTO.getMonth(), tokenUser);
        BigDecimal oldRealityAmount = empSocBackRecordInfoDO.getRealityAmount() == null ?
                new BigDecimal("0") : empSocBackRecordInfoDO.getRealityAmount();
        BigDecimal oldAccountAmount = empSocBackRecordInfoDO.getAccountAmount() == null ?
                new BigDecimal("0") : empSocBackRecordInfoDO.getAccountAmount();
        ParamException.notNull(empSocBackRecordInfoDO, "回款失败，" + backName + "该月份没有回款信息，请先前往社保页面添加回款记录!");
        EmpSocBackRecordInfoUpdateDTO empSocBackRecordInfoUpdateDTO = new EmpSocBackRecordInfoUpdateDTO();
        empSocBackRecordInfoUpdateDTO.setId(empSocBackRecordInfoDO.getId());
        // 余额扣款 账户实扣金额累加
        if (EmpSocBackStatusConstant.WAY_STATUS_ACCOUNT.equals(empSocBackRecordRecordAddDTO.getWayStatus())) {
            empSocBackRecordInfoUpdateDTO.setAccountAmount(oldAccountAmount.add(realityAmount));
        } else {
            // 非余额扣款  实收累加
            empSocBackRecordInfoUpdateDTO.setRealityAmount(oldRealityAmount.add(realityAmount));
        }
        if (totalAmount.compareTo(realityAmount) <= 0) {
            empSocBackRecordInfoUpdateDTO.setStatus(EmpSocBackStatusConstant.STATUS_THREE);
        } else {
            empSocBackRecordInfoUpdateDTO.setStatus(EmpSocBackStatusConstant.STATUS_TWO);
        }
        // 计算现在的未回款金额
        BigDecimal currentUncollectedAmount = empSocBackRecordInfoDO
                .getUncollectedAmount().subtract(realityAmount);
        if (currentUncollectedAmount.compareTo(BigDecimal.ZERO) < 0) {
            empSocBackRecordInfoUpdateDTO.setUncollectedAmount(new BigDecimal("0"));
        } else {
            empSocBackRecordInfoUpdateDTO.setUncollectedAmount(currentUncollectedAmount);
        }
        // 查询该员工的账户信息
        EmpSocBackRecordAccountDO empSocBackRecordAccountDO = empSocBackRecordAccountService
                .selectDOByEmpId(empSocBackRecordRecordAddDTO.getEmpId(), tokenUser.getSiteId());
        ParamException.notNull(empSocBackRecordAccountDO, "员工" + backName + "账户信息不存在，请刷新重试或删除后重新添加!");
        EmpSocBackRecordAccountUpdateDTO empSocBackRecordAccountUpdateDTO = new EmpSocBackRecordAccountUpdateDTO();
        empSocBackRecordAccountUpdateDTO.setId(empSocBackRecordAccountDO.getId());
        if (EmpSocBackStatusConstant.WAY_STATUS_ACCOUNT.equals(empSocBackRecordRecordAddDTO.getWayStatus())) {
            // 直接从余额扣款  实收不能大于应收  余额不足不能回款
            if (realityAmount.compareTo(totalAmount) > 0) {
                throw new ParamException(backName + " 余额扣款，实收不能大于应收!");
            }
            if (realityAmount.compareTo(empSocBackRecordAccountDO.getAmount()) > 0) {
                throw new ParamException(backName + "余额不足，回款失败!");
            }
            BigDecimal sub = empSocBackRecordAccountDO.getAmount().subtract(realityAmount);
            empSocBackRecordAccountUpdateDTO.setAmount(sub);
            empSocBackRecordRecordDO.setAmount(sub);
        } else {
            empSocBackRecordRecordDO.setAmount(empSocBackRecordAccountDO.getAmount());
            if (totalAmount.compareTo(realityAmount) < 0) {
                // 修改账户信息
                BigDecimal sub = realityAmount.subtract(totalAmount);
                BigDecimal amount = empSocBackRecordAccountDO.getAmount().add(sub);
                empSocBackRecordAccountUpdateDTO.setAmount(amount);
                empSocBackRecordRecordDO.setAmount(amount);
            }
        }
        empSocBackRecordInfoService.update(empSocBackRecordInfoUpdateDTO, tokenUser);
        empSocBackRecordAccountService.update(empSocBackRecordAccountUpdateDTO, tokenUser);
        // 保存
        empSocBackRecordRecordDO = save(empSocBackRecordRecordDO, tokenUser.getSiteId(), tokenUser.getUserId());
        // 写入账户余额变动
        backSaveRecord(empSocBackRecordRecordDO.getYear(), empSocBackRecordRecordDO.getMonth(),
                empSocBackRecordInfoDO.getCompanyId(), empSocBackRecordRecordDO.getId(),
                false, realityAmount, tokenUser);
        return empSocBackRecordRecordDO.to(EmpSocBackRecordRecordVO.class);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public Integer saveBatch(EmpSocBackRecordRecordAddDTO[] empSocBackRecordRecords, TokenUser tokenUser) {
        for (EmpSocBackRecordRecordAddDTO empSocBackRecordRecordAddDTO : empSocBackRecordRecords) {
            this.save(empSocBackRecordRecordAddDTO, tokenUser);
        }
        return empSocBackRecordRecords.length;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(EmpSocBackRecordRecordUpdateDTO empSocBackRecordRecordUpdateDTO, TokenUser tokenUser) {
        //转DO
        EmpSocBackRecordRecordDO empSocBackRecordRecordDO = empSocBackRecordRecordUpdateDTO.to(EmpSocBackRecordRecordDO.class);
        //根据主键更新，只更新非null值
        return updateByIdSelective(empSocBackRecordRecordDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, String... empSocBackRecordRecordIds) {
        // 删除回款信息  修复账户余额
        for (String empSocBackRecordRecordId : empSocBackRecordRecordIds) {
            EmpSocBackRecordRecordDO empSocBackRecordRecordDO = this
                    .selectDOById(empSocBackRecordRecordId, tokenUser.getSiteId());
            ParamException.notNull(empSocBackRecordRecordDO, "根据id没有找到该条信息，请刷新重试!");
            // 应收
            BigDecimal totalAmount = empSocBackRecordRecordDO.getTotalAmount();
            // 实收
            BigDecimal realityAmount = empSocBackRecordRecordDO.getRealityAmount();
            // 查询账户信息
            EmpSocBackRecordAccountDO empSocBackRecordAccountDO = empSocBackRecordAccountService
                    .selectDOByEmpId(empSocBackRecordRecordDO.getEmpId(), tokenUser.getSiteId());
            ParamException.notNull(empSocBackRecordAccountDO, "该员工没有账户信息，请添加回款信息后重试!");
            EmpSocBackRecordAccountUpdateDTO empSocBackRecordAccountUpdateDTO = new EmpSocBackRecordAccountUpdateDTO();
            empSocBackRecordAccountUpdateDTO.setId(empSocBackRecordAccountDO.getId());
            // 是否是余额扣款
            if (EmpSocBackStatusConstant.WAY_STATUS_ACCOUNT.equals(empSocBackRecordRecordDO.getWayStatus())) {
                // 余额回滚到账户中
                empSocBackRecordAccountUpdateDTO.setAmount(empSocBackRecordAccountDO.getAmount()
                        .add(empSocBackRecordRecordDO.getRealityAmount()));
            } else {
                if (realityAmount.compareTo(totalAmount) > 0) {
                    // 余额减去增加部分
                    empSocBackRecordAccountUpdateDTO.setAmount(empSocBackRecordAccountDO.getAmount()
                            .subtract(realityAmount.subtract(totalAmount)));
                }
            }
            empSocBackRecordAccountService.update(empSocBackRecordAccountUpdateDTO, tokenUser);
            // 写入账户余额变动
            EmpSocBackRecordInfoDO empSocBackRecordInfoDO = empSocBackRecordInfoService.selectByEmpIdAndYearAndMonth(
                    empSocBackRecordRecordDO.getEmpId(), empSocBackRecordRecordDO.getYear(),
                    empSocBackRecordRecordDO.getMonth(), tokenUser);
            backSaveRecord(empSocBackRecordRecordDO.getYear(), empSocBackRecordRecordDO.getMonth(),
                    empSocBackRecordInfoDO.getCompanyId(), empSocBackRecordRecordDO.getId(),
                    true, realityAmount.negate(), tokenUser);
        }
        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) empSocBackRecordRecordIds);
    }

    /**
     * 写入账户余额变动
     *
     * @param year         年
     * @param month        月
     * @param companyId    公司id
     * @param subjectId    业务id
     * @param deleteStatus 是否删除
     * @param changeAmount 变动金额
     * @param tokenUser    当前用户
     */
    private void backSaveRecord(String year, String month, String companyId,
                                String subjectId, boolean deleteStatus, BigDecimal changeAmount,
                                TokenUser tokenUser) {
        CompanyBalanceRecordDO companyBalanceRecordDO = new CompanyBalanceRecordDO();
        companyBalanceRecordDO.setYearly(Integer.parseInt(year));
        companyBalanceRecordDO.setMonthly(Integer.parseInt(month));
        companyBalanceRecordDO.setCompanyId(companyId);
        companyBalanceRecordDO.setChangeType(BalanceTypeEnum.SOC.getValue());
        companyBalanceRecordDO.setSubjectId(subjectId);
        if (deleteStatus) {
            companyBalanceRecordDO.setDeleteStatus(true);
        }
        companyBalanceRecordDO.setChangeAmount(changeAmount);
        companyBalanceRecordService.saveRecord(companyBalanceRecordDO, tokenUser);
    }


    @Override
    public EmpSocBackRecordRecordVO selectById(String empSocBackRecordRecordId, String siteId) {
        EmpSocBackRecordRecordDO empSocBackRecordRecordDO = selectDOById(empSocBackRecordRecordId, siteId);
        if (empSocBackRecordRecordDO == null) {
            return null;
        }
        return empSocBackRecordRecordDO.to(EmpSocBackRecordRecordVO.class);
    }

    @Override
    public List<EmpSocBackRecordRecordListVO> select(EmpSocBackRecordRecordQuery empSocBackRecordRecordQuery, String siteId) {
        List<EmpSocBackRecordRecordDO> empSocBackRecordRecordDOList = selectDO(empSocBackRecordRecordQuery, siteId);
        if (CollectionUtils.isEmpty(empSocBackRecordRecordDOList)) {
            return empSocBackRecordRecordDOList == null ? null : new ArrayList<>();
        }
        return empSocBackRecordRecordDOList.stream()
                .map(empSocBackRecordRecordDO -> empSocBackRecordRecordDO.to(EmpSocBackRecordRecordListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public int selectCount(EmpSocBackRecordRecordQuery empSocBackRecordRecordQuery, String siteId) {
        Example example = buildExample(empSocBackRecordRecordQuery, siteId);
        return empSocBackRecordRecordDAO.selectCountByExample(example);
    }

    @Override
    public Page<EmpSocBackRecordRecordListVO> selectPage(EmpSocBackRecordRecordQuery empSocBackRecordRecordQuery, String siteId) {
        Example example = buildExample(empSocBackRecordRecordQuery, siteId);
        Page<EmpSocBackRecordRecordDO> page = empSocBackRecordRecordDAO.selectPageByExample(example,
                empSocBackRecordRecordQuery.getPageNo(),
                empSocBackRecordRecordQuery.getPageSize());

        Page<EmpSocBackRecordRecordListVO> resultPage = page.to(EmpSocBackRecordRecordListVO.class);
        List<EmpSocBackRecordRecordListVO> entities = resultPage.getEntities();
        if (!CollectionUtils.isEmpty(entities)) {
            List<String> createUserIdList = entities.stream()
                    .map(EmpSocBackRecordRecordListVO::getCreateUserId).collect(Collectors.toList());
            List<UserDO> userList = userProviderService.selectUserByIds(createUserIdList);
            Map<String, String> userNameMap = userList.stream().collect(Collectors.toMap(UserDO::getId, UserDO::getName));
            for (EmpSocBackRecordRecordListVO entity : entities) {
                entity.setCreateUserName(userNameMap.get(entity.getCreateUserId()));
            }
        }
        return resultPage;
    }

    @Override
    public EmpSocBackRecordRecordDO selectDOById(String empSocBackRecordRecordId, String siteId) {
        return listById(empSocBackRecordRecordId, siteId);
    }

    @Override
    public List<EmpSocBackRecordRecordDO> selectDO(EmpSocBackRecordRecordQuery empSocBackRecordRecordQuery, String siteId) {
        Example example = buildExample(empSocBackRecordRecordQuery, siteId);
        return empSocBackRecordRecordDAO.selectByExample(example);
    }

    @Override
    public List<EmpSocBackRecordRecordVO> selectByEmpIdAndYearAndMonth(String empId, String year, String month, String siteId) {
        EmpSocBackRecordRecordQuery empSocBackRecordRecordQuery = new EmpSocBackRecordRecordQuery();
        empSocBackRecordRecordQuery.setEmpId(empId);
        empSocBackRecordRecordQuery.setYear(year);
        empSocBackRecordRecordQuery.setMonth(month);
        List<EmpSocBackRecordRecordDO> recordList = this.selectDO(empSocBackRecordRecordQuery, siteId);
        if (!CollectionUtils.isEmpty(recordList)) {
            List<String> createUserIdList = recordList.stream()
                    .map(EmpSocBackRecordRecordDO::getCreateUserId).collect(Collectors.toList());
            List<UserDO> userList = userProviderService.selectUserByIds(createUserIdList);
            Map<String, String> userNameMap = userList.stream().collect(Collectors.toMap(UserDO::getId, UserDO::getName));
            return recordList.stream().map(record -> {
                EmpSocBackRecordRecordVO recordVO = record.to(EmpSocBackRecordRecordVO.class);
                recordVO.setCreateUserName(userNameMap.get(record.getCreateUserId()));
                return recordVO;
            }).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public ImportExcelResultListVO<SocBackRecordImportDTO> importRecord(File file, EmpSocBackRecordInfoQuery query,
                                                                        TokenUser tokenUser) {
        SocBackRecordImportListener recordImportListener = BeanKit.getBean(SocBackRecordImportListener.class);
        recordImportListener.setTokenUser(tokenUser);
        recordImportListener.setParseOnly(false);
        recordImportListener.setQuery(query);
        recordImportListener.setDataList(new ArrayList<>());
        recordImportListener.setSuccessList(new ArrayList<>());
        recordImportListener.setFailList(new ArrayList<>());
        EasyExcel.read(file, SocBackRecordImportDTO.class, recordImportListener).sheet().doRead();
        ImportExcelResultListVO<SocBackRecordImportDTO> result = new ImportExcelResultListVO<>();
        List<SocBackRecordImportDTO> errorList = recordImportListener.getFailList();
        List<SocBackRecordImportDTO> dataList = recordImportListener.getDataList();
        result.setErrorList(errorList);
        result.setErrorCount(errorList.size());
        result.setAllCount(dataList.size());
        result.setSuccessCount(dataList.size() - errorList.size());
        return result;
    }

    @Override
    public void saveImportRecord(List<SocBackRecordImportDTO> importList, TokenUser tokenUser) {
        for (SocBackRecordImportDTO importData : importList) {
            EmpSocBackRecordRecordAddDTO addInfo = new EmpSocBackRecordRecordAddDTO();
            addInfo.setEmpId(importData.getBackRecordInfo().getEmpId());
            addInfo.setYear(importData.getBackRecordInfo().getYear());
            addInfo.setMonth(importData.getBackRecordInfo().getMonth());
            addInfo.setOverdueFine(importData.getBackRecordInfo().getOverdueFine());
            addInfo.setRealityAmount(new BigDecimal(importData.getAmount()));
            addInfo.setServiceCost(importData.getBackRecordInfo().getServiceCost());
            addInfo.setSocAmount(importData.getBackRecordInfo().getSocAmount());
            EmpSocBackRecordInfoVO backInfo = empSocBackRecordInfoService
                    .selectById(importData.getBackRecordInfo().getId(), tokenUser.getSiteId());
            addInfo.setTotalAmount(backInfo.getUncollectedAmount());
            addInfo.setWayStatus(importData.getWayStatus());
            addInfo.setName(importData.getName());
            save(addInfo, tokenUser);
        }
    }

    @Override
    public String errorInfoExport(ImportExcelResultListVO<SocBackRecordImportDTO> errorInfo) {
        List<SocBackRecordImportDTO> errorList = errorInfo.getErrorList();
        if (CollectionUtils.isEmpty(errorList)) {
            throw new ParamException("暂无错误数据可导出");
        }
        return ExcelKit.exportExcel("socBackErrorList", "异常数据表",
                SocBackRecordImportDTO.class, errorList);
    }

    /**
     * 根据查询参数，构建example
     *
     * @param empSocBackRecordRecordQuery 查询参数
     * @param siteId                      所属站点id
     * @return example
     */
    private Example buildExample(EmpSocBackRecordRecordQuery empSocBackRecordRecordQuery, String siteId) {
        Example example = new Example(EmpSocBackRecordRecordDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        if (empSocBackRecordRecordQuery.getEmpId() != null) {
            example.and().andEqualTo("empId", empSocBackRecordRecordQuery.getEmpId());
        }
        if (empSocBackRecordRecordQuery.getYear() != null) {
            example.and().andEqualTo("year", empSocBackRecordRecordQuery.getYear());
        }
        if (empSocBackRecordRecordQuery.getMonth() != null) {
            example.and().andEqualTo("month", empSocBackRecordRecordQuery.getMonth());
        }
        if (empSocBackRecordRecordQuery.getWayStatus() != null) {
            example.and().andEqualTo("wayStatus", empSocBackRecordRecordQuery.getWayStatus());
        }
        // 排序
        ExampleKit.setExampleOrder(example, empSocBackRecordRecordQuery.getOrders());
        return example;
    }
}




